x86 mca: unmap broken memory in EPT guest in MCA delayed handler
authorKeir Fraser <keir.fraser@citrix.com>
Mon, 13 Sep 2010 16:48:19 +0000 (17:48 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Mon, 13 Sep 2010 16:48:19 +0000 (17:48 +0100)
When a memory owned by EPT guest is broken, we change the P2M type to
be broken memory type, so that later access from the guest to the
broken memory will be trapped as EPT violation.

Signed-off-by: Jiang, Yunhong <yunhong.jiang@intel.com>
Acked-by: Tim Deegan <Tim.Deegan@citrix.com>
xen/arch/x86/cpu/mcheck/mce.h
xen/arch/x86/cpu/mcheck/mce_intel.c
xen/arch/x86/cpu/mcheck/vmce.c

index 691c2a9b14b1a3e17eb5c6aab71c9d9d4a7b64bc..ec34170ce15842820d04512d33a3fdd7f6273e9b 100644 (file)
@@ -49,6 +49,7 @@ void mce_intel_feature_init(struct cpuinfo_x86 *c);
 void amd_nonfatal_mcheck_init(struct cpuinfo_x86 *c);
 
 int is_vmce_ready(struct mcinfo_bank *bank, struct domain *d);
+int unmmap_broken_page(struct domain *d, mfn_t mfn, unsigned long gfn);
 
 u64 mce_cap_init(void);
 extern int firstbank;
index 00ee183fce50e41c76b562b02a158aa39f5d5bcf..f8de9829376f42da0b166f74ed7859f4a22f81ba 100644 (file)
@@ -654,16 +654,22 @@ static void intel_memerr_dhandler(int bnum,
             BUG_ON( result->owner == DOMID_COW );
             if ( result->owner != DOMID_XEN ) {
                 d = get_domain_by_id(result->owner);
+                ASSERT(d);
+                gfn = get_gpfn_from_mfn((bank->mc_addr) >> PAGE_SHIFT);
+
                 if ( !is_vmce_ready(bank, d) )
                 {
-                    /* Should not inject vMCE to guest */
-                    if ( d )
-                        put_domain(d);
-                    return;
+                    printk("DOM%d not ready for vMCE\n", d->domain_id);
+                    goto vmce_failed;
+                }
+
+                if ( unmmap_broken_page(d, _mfn(mfn), gfn) )
+                {
+                    printk("Unmap broken memory %lx for DOM%d failed\n",
+                            mfn, d->domain_id);
+                    goto vmce_failed;
                 }
 
-                ASSERT(d);
-                gfn = get_gpfn_from_mfn((bank->mc_addr) >> PAGE_SHIFT);
                 bank->mc_addr =  gfn << PAGE_SHIFT |
                   (bank->mc_addr & (PAGE_SIZE -1 ));
                 if ( fill_vmsr_data(bank, d,
@@ -671,18 +677,15 @@ static void intel_memerr_dhandler(int bnum,
                 {
                     mce_printk(MCE_QUIET, "Fill vMCE# data for DOM%d "
                       "failed\n", result->owner);
-                    put_domain(d);
-                    domain_crash(d);
-                    return;
+                    goto vmce_failed;
                 }
+
                 /* We will inject vMCE to DOMU*/
                 if ( inject_vmce(d) < 0 )
                 {
                     mce_printk(MCE_QUIET, "inject vMCE to DOM%d"
                       " failed\n", d->domain_id);
-                    put_domain(d);
-                    domain_crash(d);
-                    return;
+                    goto vmce_failed;
                 }
                 /* Impacted domain go on with domain's recovery job
                  * if the domain has its own MCA handler.
@@ -691,6 +694,11 @@ static void intel_memerr_dhandler(int bnum,
                  */
                 result->result = MCA_RECOVERED;
                 put_domain(d);
+
+                return;
+vmce_failed:
+                put_domain(d);
+                domain_crash(d);
             }
         }
     }
index 8c3e7bcb1cb7e87a64884f33dafa3b724c999af5..123d1dcfc76ecc25472a5c0f4e567cd939aa8f48 100644 (file)
@@ -558,3 +558,51 @@ int is_vmce_ready(struct mcinfo_bank *bank, struct domain *d)
 
     return 0;
 }
+
+/* It's said some ram is setup as mmio_direct for UC cache attribute */
+#define P2M_UNMAP_TYPES (p2m_to_mask(p2m_ram_rw) \
+                                | p2m_to_mask(p2m_ram_logdirty) \
+                                | p2m_to_mask(p2m_ram_ro)       \
+                                | p2m_to_mask(p2m_mmio_direct))
+
+/*
+ * Currently all CPUs are redenzevous at the MCE softirq handler, no
+ * need to consider paging p2m type
+ * Currently only support HVM guest with EPT paging mode
+ * XXX following situation missed:
+ * PoD, Foreign mapped, Granted, Shared
+ */
+int unmmap_broken_page(struct domain *d, mfn_t mfn, unsigned long gfn)
+{
+    mfn_t r_mfn;
+    struct p2m_domain *p2m;
+    p2m_type_t pt;
+
+    /* Always trust dom0's MCE handler will prevent future access */
+    if ( d == dom0 )
+        return 0;
+
+    if (!mfn_valid(mfn_x(mfn)))
+        return -EINVAL;
+
+    if ( !is_hvm_domain(d) || !paging_mode_hap(d) )
+        return -ENOSYS;
+
+    p2m = p2m_get_hostp2m(d);
+    ASSERT(p2m);
+
+    /* This only happen for PoD memory, which should be handled seperetely */
+    if (gfn > p2m->max_mapped_pfn)
+        return -EINVAL;
+
+    r_mfn = gfn_to_mfn_query(p2m, gfn, &pt);
+    if ( p2m_to_mask(pt) & P2M_UNMAP_TYPES)
+    {
+        ASSERT(mfn_x(r_mfn) == mfn_x(mfn));
+        p2m_change_type(p2m, gfn, pt, p2m_ram_broken);
+        return 0;
+    }
+
+    return -1;
+}
+